/**
 *    Copyright (C) 2009 Victor Smirnov <aist11@gmail.com>
 *
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License along
 *    with this program; if not, write to the Free Software Foundation, Inc.,
 *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 *    See LICENSE.exceptions in the root folder of this project for possible
 *    exceptions
 *
 */

#ifndef _CUBEFS_CORE_API_REFLECTION_OPERATION_HPP
#define _CUBEFS_CORE_API_REFLECTION_OPERATION_HPP


#include <cubefs/api/exceptions/cast.hpp>
#include <cubefs/api/exceptions/bounds.hpp>
#include <cubefs/api/exceptions/npe.hpp>

#include <cubefs/api/reflection/operation.hpp>

#include <cubefs/core/api/reflection/type.hpp>
#include <cubefs/core/api/reflection/argument.hpp>

#define CUBEFS_SOURCE "cubefs/core/api/reflection/operation.hpp:" CUBEFS_AT

namespace cubefs { namespace api {

template <typename T> struct TypedWrapperFactory;

template <typename Interface>
class OperationImplT: public TypedImplT<Interface> {
	typedef OperationImplT<Interface> 	Me;
	typedef TypedImplT<Interface>		Base;
public:

public:
    static void InitType() {
    	type_ = new AbstractType(TypeNameFactory<Interface>::name(), Typed::MyType());
    }

    static void InitOperations();

    CUBEFS_DECLARE_REFLECTION;
};

template <typename Interface>
Type* OperationImplT<Interface>::type_ 					= NULL;
typedef OperationImplT<Operation>						OperationImpl;



template <Int ArgsNum, typename Interface>
class AbstractOperationImplT: public OperationImplT<Interface> {

    typedef AbstractOperationImplT<ArgsNum, Interface> 	Me;
    typedef OperationImplT<Interface>				Base;


    Type*                           return_type_;
    Type*                           owner_;
    std::vector<const Argument*>    arguments_;
    String                     		name_;
    bool static_;
    typedef Type*                   PType;

public:

    AbstractOperationImplT(Type *owner, Type* return_type, StringRef name, bool static0 = false):
            owner_(owner), return_type_(return_type), name_(name), static_(static0) {}

    virtual ~AbstractOperationImplT() throw () {
        for (int c = 0; c < arguments_.size(); c++) {
            delete arguments_[c];
        }
    }

    virtual bool IsStatic() {
        return static_;
    }

    virtual Type* owner() const {
        return owner_;
    }

    virtual Type *GetReturnType() const {
        return return_type_;
    }

    virtual Int GetArgumentsCount() const {
        return arguments_.size();
    }

    virtual const Argument *GetArgument(Int index) const {
        if (index >= 0 && index < arguments_.size()) {
            return arguments_[index];
        }
        else {
            throw BoundsException(CUBEFS_SOURCE, index, 0, arguments_.size());
        }
    }

    virtual const Argument *GetArgument(const std::string& name) const {
        for (Int c = 0; c < arguments_.size();c++) {
            if (name == arguments_[c]->name()) {
                return arguments_[c];
            }
        }

        return NULL;
    }

    virtual const std::string &name() const {
        return name_;
    }

    void SetReturnType(PType *type) {
        return_type_ = type;
    }

    void AddArgumentType(const Argument *param) {
        arguments_.push_back(param);
    }


    void CheckTargetValue(Typed* target) const {
        if (target == NULL) throw NullPointerException(CUBEFS_SOURCE, "Target object must not be NULL");
        if (!target->IsInstance(owner_)) {
            throw TypeCastException(CUBEFS_SOURCE, target->type(), owner_);
        }
    }

    Typed* CheckReturnValue(Typed* value) const {
        if (value != NULL && !value->IsInstance(return_type_))
        {
            throw TypeCastException(CUBEFS_SOURCE, value->type(), return_type_);
        }
        return value;
    }

    void CheckArgumentTypes(StaticListOfTyped* arguments) const {
        for (Int c = 0; c < arguments_.size(); c++)
        {
            if (arguments->Get(c) != NULL)
            {
                if (!arguments->Get(c)->IsInstance(arguments_[c]->GetParameterType()))
                {
                    throw TypeCastException(CUBEFS_SOURCE, arguments->Get(c)->type(), arguments_[c]->GetParameterType());
                }
            }
        }
    }

    void CheckTargetType(Typed *target, Type* target_type) const {
        if (!target->IsInstance(target_type))
        {
            throw TypeCastException(CUBEFS_SOURCE, target->type(), target_type);
        }
    }


private:
    class InvokeOp: public Me {
        typedef Me Base0;
    public:
        InvokeOp(Type* owner): Base(owner, NULL, "invoke", this) {
            Base0::AddArgumentType(new ArgumentImpl("target", TypeFactory<Typed>::type()));
            Base0::AddArgumentType(new ArgumentImpl("parameters", TypeFactory<ListOfTyped>::type()));
        }

        virtual Typed *invoke(Typed* target, StaticListOfTyped* arguments) const
        {
            Base0::CheckTargetValue(target);
            Base0::CheckArgumentTypes(arguments);
            Base0::CheckTargetType(target, Base0::MyType());

            Operation* tgt = static_cast<Operation*>(target);
            StaticListOfTyped* args = static_cast<StaticListOfTyped*>(arguments->Get(1));

            return tgt->invoke(arguments->Get(0), args);
        }
    };
};


template <Int ArgsNum>
class AbstractOperationImpl: public AbstractOperationImplT<ArgsNum, Operation> {
	typedef AbstractOperationImplT<ArgsNum, Operation> Base;
public:
	AbstractOperationImpl(Type *owner, Type* return_type, StringRef name, bool static0 = false):
		Base(owner, return_type, name, static0){}
};



template <typename T>
struct ParameterTypeHelper {
    static T convert(Typed* value) {
        if (value == NULL) throw NullPointerException(CUBEFS_SOURCE, "Parameter must not be null");
        return static_cast<TypedValue<T>*>(value)->GetValue();
    }
};

template <>
struct ParameterTypeHelper<Typed*> {
    static Typed* convert(Typed* value) {
        return value;
    }
};


#define CUBEFS_DEFINE_PARAMETER_TYPE(TypeName)      \
template <>                                         \
struct ParameterTypeHelper<TypeName*> {             \
    static TypeName* convert(Typed* value) {        \
        return static_cast<TypeName*>(value);       \
    }                                               \
}


}}

#undef CUBEFS_SOURCE
#endif
